﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LogLibrary
{
    public delegate void LogEventDelegate(string Text);
    public class EventLogger
    {

        #region Logging Events
        public event LogEventDelegate Error;
        public event LogEventDelegate Status;
        public event LogEventDelegate Action;
        public event LogEventDelegate Detail;
        #endregion

        #region Declarations

        private List<LogEvent> moEvents;
        private System.IO.StreamWriter logFile;
        private string logFileName;
        private string fileName;

        #endregion

        #region Constructor



        #region Log - Singleton Pattern

        private static EventLogger defaultLog;
        private static string defaultLogName = "";

        public static string DefaultLogName
        {
            get
            {
                if (defaultLogName == "")
                {
                    return "log";
                }
                else
                {
                    return defaultLogName;
                }
            }
            set
            {
                if (defaultLogName != value)
                {
                    defaultLogName = value;
                    defaultLog = null;
                }
            }
        }

        public static EventLogger Log
        {
            get
            {
                if (defaultLog == null)
                {
                    defaultLog = new EventLogger(DefaultLogName);
                }
                return defaultLog;
            }
        }

        #endregion

        public EventLogger(string FileName)
        {
            SetSettings(FileName);
        }

        private void SetSettings(string FileName)
        {
            fileName = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(GetType().Assembly.Location), FileName);
            logFileName = System.IO.Path.ChangeExtension(fileName, ".log");
            moEvents = new List<LogEvent>();
        }
        #endregion

        public void LogStatus(string Message)
        {
            lock (moEvents)
            {
                moEvents.Add(new LogEvent(LogEvent.CATEGORY_STATUS, Message));
                if (Status != null)
                {
                    Status(Message);
                }
                StartLogging();
            }
        }

        public void LogError(Exception ex)
        {
            LogError(ex.Message);
        }

        public void LogError(string Message)
        {
            lock (moEvents)
            {
                moEvents.Add(new LogEvent(LogEvent.CATEGORY_ERROR, Message));
                if (Error != null)
                {
                    Error(Message);
                }
                StartLogging();
            }
        }

        public void LogAction(string Message)
        {
            lock (moEvents)
            {
                moEvents.Add(new LogEvent(LogEvent.CATEGORY_ACTION, Message));
                if (Action != null)
                {
                    Action(Message);
                }
                StartLogging();
            }
        }

        public void LogDetail(string Message)
        {
            lock (moEvents)
            {
                moEvents.Add(new LogEvent(LogEvent.CATEGORY_DETAIL, Message));
                if (Detail != null)
                {
                    Detail(Message);
                }
                StartLogging();
            }
        }

        #region Worker Threads for Logging

        private System.Threading.Thread moLogThread;
        private object threadSync = new object();
        private object fileSync = new object();
        private void StartLogging()
        {
            try
            {
                lock (threadSync)
                {
                    if (moLogThread == null || moLogThread.ThreadState != System.Threading.ThreadState.Running)
                    {
                        System.Threading.ThreadStart t = new System.Threading.ThreadStart(LogWorker);
                        moLogThread = new System.Threading.Thread(t);
                        moLogThread.Start();
                    }
                }
            }
            catch
            {
                System.Diagnostics.Debug.WriteLine("Error in StartLogging");
            }
        }

        private void CreateLogFile()
        {
            try
            {
                // TODO: Check the size of the Log file and back up if too big
                logFile = new System.IO.StreamWriter(logFileName, true);
            }
            catch
            {
                System.Diagnostics.Debug.WriteLine("Error Creating Logfile");
            }
        }



        private void LogWorker()
        {
            try
            {
                lock (fileSync)
                {
                    CreateLogFile();
                    while (WriteToLog())
                    {
                    }
                    if (logFile != null)
                    {
                        logFile.Close();
                        logFile = null;
                    }
                }
            }
            catch
            {
                System.Diagnostics.Debug.WriteLine("Error creating log file");
            }

        }

        private bool WriteToLog()
        {
            lock (moEvents)
            {
                if (moEvents.Count > 0)
                {
                    try
                    {
                        moEvents[0].WriteToFile(logFile);
                    }
                    catch
                    {
                        System.Diagnostics.Debug.WriteLine("Error Writing to Logfile");
                    }
                    finally
                    {
                        moEvents.RemoveAt(moEvents.Count - 1);
                    }
                }
            }
            return moEvents.Count > 0;
        }

        #endregion
    }
}
